﻿using IdentityServer4.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Shared.JwtHelper;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using Users.DTO;
using Users.IDomain;

namespace Users.Domain
{
    /// <summary>
    /// Token
    /// </summary>
    public class Authenticate : IAuthenticate
    {
        private readonly TokenManagement m_TokenManagement;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="tokenManagement">appsettings.json中 token 相关配置获取</param>
        public Authenticate(IOptions<TokenManagement> tokenManagement)
        {
            m_TokenManagement = tokenManagement.Value;
        }
        /// <summary>
        /// 颁发Token
        /// </summary>
        /// <param name="user"></param>
        /// <param name="token"></param>
        /// <returns>
        /// 继承 ControllerBase 的可以通过 User.GetDisplayName(); 这个方法获取登录名
        /// var claims = this.User.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
        /// </returns>
        public JwtToken TokenAuthenticated(User user)
        {
            var claims = new[]
            {
                new Claim(ClaimTypes.Name, user.Username),
                new Claim(ClaimTypes.NameIdentifier, user.ID),
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(m_TokenManagement.Secret));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var expires = DateTime.Now.AddMinutes(m_TokenManagement.AccessExpiration);
            var jwtToken = new JwtSecurityToken(m_TokenManagement.Issuer
                                              , m_TokenManagement.Audience
                                              , claims
                                              , expires: expires
                                              , signingCredentials: credentials);

            return new JwtToken { token = new JwtSecurityTokenHandler().WriteToken(jwtToken), Expires = expires };
        }

        #region 附带刷新token 的做法 https://www.cnblogs.com/wyt007/p/11459547.html
        //要想做访问Token 与 刷新Token 不同 ,需要将 认证与业务系统分开才能实现,因为两个的 token 的认证方不同,密钥也不同 

        ///// <summary>
        ///// 根据用户创建Token
        ///// </summary>
        ///// <param name="user"></param>
        ///// <returns></returns>
        //public Token CreateToken(User user)
        //{
        //    Claim[] claims =
        //    {
        //        new Claim(ClaimTypes.NameIdentifier, user.Code),
        //        new Claim(ClaimTypes.Name, user.Username)
        //    };
        //    return CreateToken(claims);
        //}
        //private Token CreateToken(Claim[] claims)
        //{
        //    var key = Encoding.UTF8.GetBytes(m_TokenManagement.Secret);
        //    var now = DateTime.Now;
        //    var expires = now.AddMinutes(m_TokenManagement.AccessExpiration);
        //    var token = new JwtSecurityToken
        //        (
        //            issuer: m_TokenManagement.Issuer,
        //            audience: m_TokenManagement.Audience,
        //            claims: claims,
        //            notBefore: now,
        //            expires: expires,
        //            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key)
        //                                                            , SecurityAlgorithms.HmacSha256)
        //        );
        //    return new Token
        //    {
        //        TokenContent = new JwtSecurityTokenHandler().WriteToken(token),
        //        Expires = expires
        //    };
        //}

        //public ComplexToken CreateToken(User user)
        //{
        //    Claim[] claims = new Claim[]
        //    { 
        //        new Claim(ClaimTypes.NameIdentifier, user.Code),
        //        new Claim(ClaimTypes.Name, user.Username)
        //    };

        //    return CreateToken(claims);
        //}

        //public ComplexToken CreateToken(Claim[] claims)
        //{
        //    return new ComplexToken
        //    {
        //        AccessToken = CreateToken(claims, TokenType.AccessToken),
        //        RefreshToken = CreateToken(claims, TokenType.RefreshToken)
        //    };
        //}

        ///// <summary>
        ///// 用于创建AccessToken和RefreshToken。
        ///// 这里AccessToken和RefreshToken只是过期时间不同，【实际项目】中二者的claims内容可能会不同。
        ///// 因为RefreshToken只是用于刷新AccessToken，其内容可以简单一些。
        ///// 而AccessToken可能会附加一些其他的Claim。
        ///// </summary>
        ///// <param name="claims"></param>
        ///// <param name="tokenType"></param>
        ///// <returns></returns>
        //private Token CreateToken(Claim[] claims, TokenType tokenType)
        //{
        //    var now = DateTime.Now;
        //    //设置不同的过期时间
        //    var expires = now.Add(TimeSpan.FromMinutes(tokenType.Equals(TokenType.AccessToken)
        //                                ? m_TokenManagement.AccessExpiration
        //                                : m_TokenManagement.RefreshTokenExpiresMinutes)
        //                         );

        //    //设置不同的接受者
        //    string audience = tokenType.Equals(TokenType.AccessToken)
        //                        ? m_TokenManagement.Audience
        //                        : m_TokenManagement.RefreshTokenAudience;

        //    var key = Encoding.UTF8.GetBytes(m_TokenManagement.Secret);

        //    var token = new JwtSecurityToken(
        //        issuer: m_TokenManagement.Issuer,
        //        audience: audience,
        //        claims: claims,
        //        notBefore: now,
        //        expires: expires,
        //        signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256));
        //    return new Token { TokenContent = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
        //}

        //public Token RefreshToken(ClaimsPrincipal claimsPrincipal)
        //{
        //    var code = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
        //    if (null != code)
        //    {
        //        return CreateAccessToken(claimsPrincipal.GetUser(code.Value.ToString()));
        //    }
        //    else
        //    {
        //        return null;
        //    }
        //}
        #endregion

    }
}
